package feng.demo.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.text.SimpleDateFormat;
import java.time.Duration;

//行为类对象 --》IOC容器（单列）
//实体类 数据库
@Configuration
public class MyRedisConfig {
    @Autowired(required = false)
    ObjectMapper om;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

//        System.out.println("我指定的redis");

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer(Object.class);

//        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //访问属性，包括getter和setter方法
        om.setDateFormat( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //日期的包名和类名

        //json数据带类的名称 日期类型（包名）
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);
        //这条语句很关键, 反序列化时, 需要知道 Java 对象类名称（字段名）

        jackson2JsonRedisSerializer.setObjectMapper(om);

        template.setConnectionFactory(factory);
        //key序列化方式（班级编号 clz:001） 或者课程编号
        template.setKeySerializer(redisSerializer);// Redis 主键的key
        //value序列化 (班级对象 或者 课程对象 成员变量)
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashKeySerializer(redisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    //重新装配了自动缓存的Json序列化，使用后会覆盖不用原生的自动序列化配置（实现IOC自动装配原理）
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        //一个序列化器,可以把 Java 的 String 类型对象序列化为 Redis 能够存储的字节数组，同时也可以把 Redis 里的字节数组反序列化为 Java 的 String 类型对象

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //也是一个序列化器，他能够把 Java 对象序列化为 JSON 格式的字节数组，并且能将 JSON 格式的字节数组反序列化为 Java 对象
        //Object,class 因为Object类们可以让这个序列化器处理多种类型的Java对象，在存储复杂的 Java 对象，使用这个序列化器可以让对象以 JSON 格式存储在 Redis 中，方便数据的存储和读取


        //解决查询缓存转换异常的问题
//        ObjectMapper om = new ObjectMapper();
        //ObjectMapper 是 Jackson 库中用来实现 Java 对象和 JSON 数据相互转换的，可以通过这个实例化进行序列化和反序列化

        //访问属性，包括getter和setter方法
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //om.setDateFormat( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        //om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        //json数据带类的名称
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);
        //这条语句很关键, 反序列化时, 需要知道 Java 对象类名称

        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化（解决乱码的问题）,过期时间120秒
        RedisCacheConfiguration config =
                RedisCacheConfiguration.defaultCacheConfig()//获取Redis缓存默认的配置，可以进行自定义修改
                        .entryTtl(Duration.ofSeconds(120))//缓存的过期时间
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//通常以字符串的形式存入Redis缓存中
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))//用于将Java对象序列化转为JSON格式存入在Redis中
                        .disableCachingNullValues();//禁止缓存null值,当方法的返回值为null时，不会存入到Redis中，避免不必要的缓存占用

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)//用于建立与Redis服务器的连接
                .cacheDefaults(config)//将配置好的RedisCacheConfiguration对象设置所有缓存默认的配置
                .build();//构建返回实例
        return cacheManager;
    }

}
